
'''
蔚来 NIO WPC053、WPC053—1
下线电检自制程序
baichao 2024.10
'''


import sys
import json

from Ui_window import Ui_MainWindow
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot

from libTSCANAPI import *
from uds import *


'''
ui主线程只只负责UI界面的显示,不负责后台任务的处理
'''
# ui主窗口
class MyWindow(QMainWindow, Ui_MainWindow):
    ui_signal_connect_result = pyqtSignal(str)
    ui_signal_showReadBaseInfo = pyqtSignal(str)
    ui_signal_showWriteResult = pyqtSignal(str)
    ui_signal_showUiResult = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.initUI()

        self.appFilePath = ""
        self.driverFilePath = ""

    # UI初始化
    def initUI(self):
        self.ui = Ui_MainWindow()
        self.ui.setupUi(MainWindow)

        initialize_lib_tscan(True, True, False)
        self.HWHandle = c_size_t(0)

        # 初始化进度条
        self.progressBar = QProgressBar(self)

        # 初始状态置灰的按钮
        self.ui.btn_disConnectTS.setEnabled(False)
        self.ui.btn_Charge.setEnabled(False)
        self.ui.btn_ECU_reset.setEnabled(False)
        self.ui.btn_readBaseInfo.setEnabled(False)
        self.ui.btn_write_fan_speed.setEnabled(False)
        self.ui.btn_write_fan_stop.setEnabled(False)
        self.ui.btn_QValue_adjust.setEnabled(False)
        self.ui.btn_fastChargeSwitchVerify.setEnabled(False)
        self.ui.checkBox_cycle.setChecked(False)


        # 绑定信号与槽:
        self.ui.btn_connectTS.clicked.connect(self.uiConnect)              # 连接同星
        self.ui.btn_disConnectTS.clicked.connect(self.uiDisConnect)        # 断开同星
        self.ui_signal_connect_result.connect(self.showConnectStatus)      # 显示连接状态
        self.ui.checkBox_cycle.stateChanged.connect(self.uiCheckBox_cycle) # 是否选中"周期读取"的状态
        self.ui_signal_showReadBaseInfo.connect(self.showBaseInfo)         # 显示基础诊断信息
        self.ui_signal_showWriteResult.connect(self.showWriteResult)
        self.ui_signal_showUiResult.connect(self.showUiResult)


        self.ui.btn_Charge.clicked.connect(self.uiChargeOnOff)             # 开/关充电
        self.ui.btn_ECU_reset.clicked.connect(self.uiResetECU)             # ECU复位
        self.ui.btn_readBaseInfo.clicked.connect(self.uireadBaseInfo)      # 刷新基础信息
        self.ui.btn_write_ECU_manufacture_date.clicked.connect(self.uiWriteECUManufactureDate) # 写ECU制造日期
        self.ui.btn_write_SN.clicked.connect(self.uiWriteSN)               # 写SN
        self.ui.btn_write_HW_part_number.clicked.connect(self.uiWriteHWPartNumber)       # 写硬件PN
        self.ui.btn_write_xiamo_key.clicked.connect(self.uiwriteXiaomiKey)  # 写小米 key
        self.ui.btn_write_OPPO_key.clicked.connect(self.uiWriteOPPOKey)     # 写OPPO key
        self.ui.btn_write_fan_speed.clicked.connect(self.uiWriteFanSpeed)   # 写风扇速度
        self.ui.btn_write_fan_stop.clicked.connect(self.uiWriteFanStop)     # 写风扇停止命令
        self.ui.btn_QValue_adjust.clicked.connect(self.uiQValueAdjust)      # Q值校准

        self.ui.btn_fastChargeSwitchVerify.clicked.connect(self.uiFastChargeSwitchVerify)  # 快充开关

        self.ui.btn_load_app.clicked.connect(self.choiceAppFile)              # 选择app文件
        self.ui.btn_load_driver.clicked.connect(self.choiceDriverFile)  
        self.ui.btn_flash.clicked.connect(self.uiFlash)                     # 刷写


        # 创建一个子线程处理后台任务
        # 同时把信号、同星句柄传递给子线程
        signal_list = [ self.ui_signal_connect_result,
                        self.ui_signal_showReadBaseInfo,
                        self.ui_signal_showWriteResult,
                        self.ui_signal_showUiResult,
                        ]
        self.work_thread = WorkThread(self.HWHandle, signal_list)


        # 子线程信号 与 子线程槽函数连接:
        self.work_thread.work_signal_connect.connect(self.work_thread.connect)
        self.work_thread.work_signal_disconnect.connect(self.work_thread.disconnect)
        self.work_thread.work_signal_chargeOnOff.connect(self.work_thread.chargeOnOff)
        self.work_thread.work_signal_reset_ECU.connect(self.work_thread.resetECU)
        self.work_thread.work_signal_read.connect(self.work_thread.handleReadBaseInfo)
        self.work_thread.work_signal_write_ECU_manufacture_date.connect(self.work_thread.writeECUManufactureDate)
        self.work_thread.work_signal_write_ASC_number.connect(self.work_thread.writeASCNumber)
        self.work_thread.work_signal_write_fast_charge_key.connect(self.work_thread.writeFastChargeKey)
        self.work_thread.work_signal_write_fan_speed.connect(self.work_thread.writeFanSpeed)
        self.work_thread.work_signal_QValue_Adjust.connect(self.work_thread.QValueAdjust)
        self.work_thread.work_signal_fastChargeSwitchVerify.connect(self.work_thread.fastChargeSwitchVerify)
        self.work_thread.work_signal_flash.connect(self.work_thread.handleFlash)

        # 启动子线程
        self.work_thread.start()


    # 连接同星
    @pyqtSlot()
    def uiConnect(self):
        # 连接之前先确认用户是否选择了ECU型号
        type_index = self.ui.comboBox_check_ECU_type.currentIndex() # int类型
        print("type_index:", type_index)
        ECU_type_list = ["null", "WPC053", "WPC053_1"]
        ECU_type = ECU_type_list[type_index]

        if ECU_type == "null":
            self.ui.label_top.setStyleSheet("color: red;")
            self.ui.label_top.setText("请先选择ECU型号 !!!")
        else:
            str = json.dumps({"ECU_type" : ECU_type})
            # 通知子线程中的connect函数
            self.work_thread.work_signal_connect.emit(str)


    # 断开同星连接
    @pyqtSlot()
    def uiDisConnect(self):
        str = json.dumps({"cmd" : "disConnect"})
        # 通知子线程中的disconnect函数
        self.work_thread.work_signal_disconnect.emit(str)  


    # 显示同星的连接状态
    @pyqtSlot(str)
    def showConnectStatus(self, str):
        str = json.loads(str)
        res = str["res"]
        msg = str["msg"]
        self.ui.label_top.setText(msg) # 将子线程返回的执行结果显示在label上
        if res == "connect success":
            self.ui.btn_connectTS.setEnabled(False)
            self.ui.btn_disConnectTS.setEnabled(True)
            self.ui.btn_Charge.setEnabled(True)
            self.ui.btn_ECU_reset.setEnabled(True)
            self.ui.btn_readBaseInfo.setEnabled(True)
            self.ui.btn_write_fan_speed.setEnabled(True)
            self.ui.btn_write_fan_stop.setEnabled(True)
            self.ui.btn_QValue_adjust.setEnabled(True)
            self.ui.btn_Charge.setText("开启充电")
        if res == "connect fail":
            self.ui.btn_connectTS.setEnabled(True)
            self.ui.btn_disConnectTS.setEnabled(False)
            self.ui.btn_Charge.setEnabled(False)
        if res == "disconnect success":
            self.ui.btn_connectTS.setEnabled(True)
            self.ui.btn_disConnectTS.setEnabled(False)
            self.ui.btn_Charge.setEnabled(False)
            self.ui.btn_readBaseInfo.setEnabled(False)
            self.ui.btn_write_fan_speed.setEnabled(False)
            self.ui.btn_write_fan_stop.setEnabled(False)
            self.ui.btn_QValue_adjust.setEnabled(False)
            self.ui.btn_Charge.setText("开启充电")
        if res == "disconnect fail":
            self.ui.btn_connectTS.setEnabled(False)
            self.ui.btn_disConnectTS.setEnabled(True)
            self.ui.btn_Charge.setEnabled(True)

    
    @pyqtSlot()
    def uiCheckBox_cycle(self):
        if self.ui.checkBox_cycle.isChecked():
            self.ui.spinBox_cycle_time.setEnabled(False)
        else:
            self.ui.spinBox_cycle_time.setEnabled(True)


    # 开/关充电
    @pyqtSlot()
    def uiChargeOnOff(self):
        if self.ui.btn_Charge.text() == "开启充电":
            str = json.dumps({"cmd" : "chargeON"})
            self.ui.btn_Charge.setText("关闭充电")
            self.ui.label_top.setText("充电功能已打开")
        else:
            str = json.dumps({"cmd" : "chargeOFF"})
            self.ui.btn_Charge.setText("开启充电")
            self.ui.label_top.setText("充电功能已关闭")
        # 通知子线程中的chargeOnOff函数
        self.work_thread.work_signal_chargeOnOff.emit(str)


    # 复位ECU
    @pyqtSlot()
    def uiResetECU(self):
        str = json.dumps({"cmd" : "resetECU"})
        # 通知子线程中的resetECU函数
        self.work_thread.work_signal_reset_ECU.emit(str)


    # 刷新基础诊断
    @pyqtSlot()
    def uireadBaseInfo(self):
        if self.ui.checkBox_cycle.isChecked():
            cycle_time = self.ui.spinBox_cycle_time.value()
            str = json.dumps({"is_cycle" : True, "cycle_time" : cycle_time})
            self.work_thread.work_signal_read.emit(str)
        else:
            str = json.dumps({"is_cycle" : False, "cycle_time" : 0})
            self.work_thread.work_signal_read.emit(str)



    # 显示基础诊断信息
    @pyqtSlot(str)
    def showBaseInfo(self, str):
        str = json.loads(str)
        SW_version = str["SW_version"]
        HW_version = str["HW_version"]
        BT_version = str["BT_version"]
        lux_version = str["lux_version"]
        HW_part_number = str["HW_part_number"]
        SW_part_number = str["SW_part_number"]
        SN = str["SN"]
        supplier_ID = str["supplier_ID"]
        ECU_name = str["ECU_name"]
        NTC_temperature = str["NTC_temperature"]
        coil_Q_value = str["coil_Q_value"]
        ECU_manufacture_date = str["ECU_manufacture_date"]
        ECU_serial_number = str["ECU_serial_number"]
        fan_speed = str["fan_speed"]
        coil_QLC = str["coil_QLC"]
        xiaomi_key = str["xiaomi_key"]
        OPPO_key = str["OPPO_key"]
        private_key_status_and_fast_charging_switch = str["private_key_status_and_fast_charging_switch"]

        # 将子线程返回的执行结果显示在label上
        self.ui.label_SW_ver.setText(SW_version)
        self.ui.label_HW_ver.setText(HW_version)
        self.ui.label_BT_ver.setText(BT_version)
        self.ui.label_lux_ver.setText(lux_version)
        self.ui.label_HW_part_number.setText(HW_part_number)
        self.ui.label_SW_part_number.setText(SW_part_number)
        self.ui.label_SN.setText(SN)
        self.ui.label_supplier_ID.setText(supplier_ID)
        self.ui.label_ECU_name.setText(ECU_name)
        self.ui.label_NTC_temperature.setText(NTC_temperature)
        self.ui.label_coil_Q_value.setText(coil_Q_value)
        self.ui.label_ECU_manufacture_date.setText(ECU_manufacture_date)
        self.ui.label_ECU_serial_number.setText(ECU_serial_number)
        self.ui.label_fan_speed.setText(fan_speed)
        self.ui.label_coil_QLC.setText(coil_QLC)
        self.ui.label_xiaomi_key.setText(xiaomi_key)
        self.ui.label_OPPO_key.setText(OPPO_key)
        self.ui.label_private_key_status_and_fast_charging_switch.setText(private_key_status_and_fast_charging_switch)

    # 显示写入数据的结果
    @pyqtSlot(str)
    def showWriteResult(self, str):
        str = json.loads(str)
        write_result = str['write_result']
        if "失败" in write_result:
            self.ui.label_write_result.setStyleSheet("color: red;") # 失败提示，用红色字体
        else:
            self.ui.label_write_result.setStyleSheet("color: black;") # 成功提示，用黑色字体
        self.ui.label_write_result.setText(write_result)


    #写ECU制造日期
    @pyqtSlot()
    def uiWriteECUManufactureDate(self):
        str_date = self.ui.dateEdit.date().toString("yy-MM-dd")
        year, month, day = map(int, str_date.split('-'))  # int 类型
        str = json.dumps({"year" : year, "month" : month, "day" : day})
        # 通知子线程中的 writeECUManufactureDate 槽函数
        self.work_thread.work_signal_write_ECU_manufacture_date.emit(str)


    #写ECU流水编号
    @pyqtSlot()
    def uiWriteSN(self):
        str_SN = self.ui.lineEdit_SN.text()
        str = json.dumps({"str_input" : str_SN, "ASC_type":"SN"})
        # 通知子线程中的 writeASCNumber 槽函数
        self.work_thread.work_signal_write_ASC_number.emit(str)


    #写硬件PN码
    @pyqtSlot()
    def uiWriteHWPartNumber(self):
        str_HW_part_number = self.ui.lineEdit_HW_part_number.text()
        str = json.dumps({"str_input" : str_HW_part_number, "ASC_type":"HW_part_number"})
        # 通知子线程中的 writeASCNumber 槽函数
        self.work_thread.work_signal_write_ASC_number.emit(str)


    #写小米 key
    @pyqtSlot()
    def uiwriteXiaomiKey(self):
        str_xiaomi_key = self.ui.lineEdit_xiaomi_key.text()
        str = json.dumps({"str_fast_charge_key" : str_xiaomi_key, "phone" : "xiaomi"})
        self.work_thread.work_signal_write_fast_charge_key.emit(str)

    #写OPPO key
    @pyqtSlot()
    def uiWriteOPPOKey(self):
        str_OPPO_key = self.ui.lineEdit_OPPO_key.text()
        str = json.dumps({"str_fast_charge_key" : str_OPPO_key, "phone" : "OPPO"})
        self.work_thread.work_signal_write_fast_charge_key.emit(str)


    #写风扇转速
    @pyqtSlot()
    def uiWriteFanSpeed(self):
        str_fan_speed = self.ui.lineEdit_fan_speed.text()
        str = json.dumps({"str_fan_speed" : str_fan_speed})
        self.work_thread.work_signal_write_fan_speed.emit(str)


    #写风扇停止命令
    @pyqtSlot()
    def uiWriteFanStop(self):
        str = json.dumps({"str_fan_speed" : "stop"})
        self.work_thread.work_signal_write_fan_speed.emit(str)



    # Q值校准
    @pyqtSlot()
    def uiQValueAdjust(self):
        if self.ui.btn_Charge.text() == "开启充电":  # 说明当前充电是关闭的,可以进行校准
            str = json.dumps({"cmd" : "QValueAdjust"})
            self.work_thread.work_signal_QValue_Adjust.emit(str)
        else:
            self.ui.label_QValue_adjust_result.setStyleSheet("color: red;")
            self.ui.label_QValue_adjust_result.setText("请先关闭充电")



    # 快速充电开关
    @pyqtSlot()
    def uiFastChargeSwitchVerify(self):
        # index 从1开始计算, 所以-1, 0 表示off, 1 表示on
        switch_xiaomi = self.ui.comboBox_fastChargeSwitch_Xiaomi.currentIndex() - 1
        switch_OPPO = self.ui.comboBox_fastChargeSwitch_OPPO.currentIndex() - 1
        switch_VIVO = self.ui.comboBox_fastChargeSwitch_VIVO.currentIndex() -1
        switch_HUAWEI = self.ui.comboBox_fastChargeSwitch_HUAWEI.currentIndex() - 1
        switch_Honor = self.ui.comboBox_fastChargeSwitch_Honor.currentIndex() - 1
        str = json.dumps({  "switch_xiaomi" : switch_xiaomi,
                            "switch_OPPO" : switch_OPPO,
                            "switch_VIVO" : switch_VIVO,
                            "switch_HUAWEI" : switch_HUAWEI,
                            "switch_Honor" : switch_Honor })
        self.work_thread.work_signal_fastChargeSwitchVerify.emit(str)


    def choiceAppFile(self):
        filePath, filetype = QFileDialog.getOpenFileName(self,
                                                        "选取文件",
                                                        "C:/",  # 起始路径
                                                        "All Files (*);;Text Files (*.s19);;Text Files (*.hex)")  # 设置文件扩展名过滤,用双分号间隔
        fileName = os.path.basename(filePath)          # 文件名
        self.ui.label_app_file_path.setText(fileName)  # ui显示文件名
        self.appFilePath = filePath


    def choiceDriverFile(self):
        filePath, filetype = QFileDialog.getOpenFileName(self,
                                                        "选取文件",
                                                        "C:/",  # 起始路径
                                                        "All Files (*);;Text Files (*.hex);;Text Files (*.s19)")  # 设置文件扩展名过滤,用双分号间隔
        fileName = os.path.basename(filePath)
        self.ui.label_driver_file_path.setText(fileName)  # ui显示文件名
        self.driverFilePath = filePath

    # 刷写
    @pyqtSlot()
    def uiFlash(self):
        if self.appFilePath == "" or self.driverFilePath == "":
            reply = QMessageBox.information(self, "提示", "请先选择文件", QMessageBox.Ok)
            return
        str = json.dumps({"app_file_path" : self.appFilePath, "driver_file_path" : self.driverFilePath})
        self.work_thread.work_signal_flash.emit(str)


    # 显示界面结果
    def showUiResult(self, str):
        str = json.loads(str)
        result_type = str['result_type']

        # 显示弹窗提示
        if result_type == 'msgBox':  
            error_describle = str['msg']
            reply = QMessageBox.information(self, "提示", error_describle, QMessageBox.Ok)  

        # 显示进度条
        if result_type == 'flash_progress':
            progress_value = str['progress_value']
            self.ui.progressBar.setValue(progress_value)  
            if progress_value == 100:
                reply = QMessageBox.information(self, "提示", '刷写成功', QMessageBox.Ok) # 弹窗



if __name__ == '__main__':
    # time.sleep(5)
    app = QApplication(sys.argv)  # 创建应用程序
    MainWindow = QMainWindow()
    test = MyWindow()
    MainWindow.show()

    sys.exit(app.exec_())  # 程序执行循环

    # 释放同星库资源
    finalize_lib_tscan()
    logger.info("释放库资源")



